package com.apestech.framework.web.lib;

import com.apestech.framework.db.SqlResultSet;
import com.apestech.framework.file.callback.ReadBufferFileCallBackHandler;
import com.apestech.framework.json.JSON;
import com.apestech.framework.json.JSONArray;
import com.apestech.framework.json.JSONObject;
import com.apestech.framework.util.StringUtil;
import com.apestech.framework.xml.sql.SqlNodeVar;

import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.util.ArrayList;
import java.util.Map;

/**
 * @author hup
 * @create 2018-10-25-9:32
 */
public class ResponseWriter extends StringUtil{

    private boolean withDate=true;
    private String head=null;
    private String body=null;
    private String code=null;
    private String codeId=null;
    private boolean headMsg;
    private String msg;
    private String objKey;
    private JSONObject headjson=new JSONObject();
    private JSONObject bodyjson=new JSONObject();
    private JSONObject json=new JSONObject();

    private HttpServletResponse response;
    private String CON_JSON="text/html;charset=utf-8";
    private String CON_BLOB="application/octet-stream;charset=utf-8";

    private boolean bigData=false;
    private String charset="UTF-8";
    private String wcharset="GBK";

    private BufferJSONWrite write;
    private WriteBody writeBody;

    public ResponseWriter(HttpServletResponse response){
        init(response, 0);
    }

    public ResponseWriter(HttpServletResponse response, int k){
        init(response, k);
    }

    private void init(HttpServletResponse response, int k){
        this.response=response;
        this.response.addHeader("Access-Control-Allow-Origin","*");
        k=1;
        if(k>0){
            this.response.setBufferSize(k*1024);
        }
    }

    public void setWithDate(boolean withDate) {
        this.withDate = withDate;
    }

    public void setObjKey(String objKey) {
        this.objKey = objKey;
    }

    public void setCharset(String charset) {
        this.charset = charset;
    }

    public void setWcharset(String wcharset) {
        this.wcharset = wcharset;
    }

    public void setSysid(String sysid){
        switch (sysid){
            case "ERP":{
                head=null;
                body="obj";
                objKey="rows";
                headMsg=false;
                msg="msg";
                codeId="success";
                code="true";
                break;
            }
            case "ERP_V":{
                head=null;
                body="obj";
                objKey=null;
                headMsg=false;
                msg="msg";
                codeId="success";
                code="true";
                break;
            }
            case "ESB":{
                head="header";
                body="body";
                objKey="content";
                headMsg=true;
                msg="message";
                codeId="code";
                code="success";
                break;
            }
        }
    }

    private void writeKey(String key, Object value, boolean ishead){
        boolean flag=false;
        if(key!=null){
            if(ishead){
                if(head!=null) headjson.put(key, value);
                else flag=true;
            } else {
                if(body!=null) bodyjson.put(key, value);
                else flag=true;
            }
        } else flag=true;
        if(flag){
            if(value instanceof Map){
                json.putAll((Map) value);
            } else {
                json.put(key, value);
            }
        }
    }

    private void writeMsg(Object code, String msg){
        if(this.code!=null && code!=null){
            if("true".equals(code)) code=this.code;
            writeKey(this.codeId, code, headMsg);
        }
        if(msg!=null){
            writeKey(this.msg, msg, headMsg);
        }
    }

    private void writeHead(JSONObject param){
        if(head!=null){
            if(param!=null) headjson.putAll(param);
            json.put(head, headjson);
        } else{
            if(param!=null) json.putAll(param);
        }
    }

    private void writeBodyJson(JSONObject param){
        JSONObject bjson= (JSONObject) json.get(body);
        if(bjson!=null){
            if(param!=null) bjson.putAll(param);
            json.put(body, bjson);
        } else {
            if(body==null){
                if(param!=null) json.putAll(param);
            } else {
                if(param!=null) {
                    json.put(body, param);
                    json.putAll(bodyjson);
                } else {
                    json.put(body, bodyjson);
                }
            }
        }
        if(bodyjson.size()>0){
            json.putAll(bodyjson);
        }
    }

    private void writeWebJson(String text) throws Exception {
        response.setContentType(CON_JSON);
        response.getOutputStream().write(text.getBytes(charset));
        response.getOutputStream().flush();
        response.getOutputStream().close();
    }

    public void writeError(Object code, String msg) throws Exception {
        writeMsg(code, msg);
        if(headjson!=null){
            writeHead(null);
        } else {
            writeBodyJson(null);
        }
        writeWebJson(JSON.toJSONString(json));
    }

    public void writeJSON(String code, String msg, Object obj) throws Exception {
        writeMsg(code, msg);
        if(objKey==null){
            json.put(body, obj);
        } else {
            bodyjson.put(objKey, obj);
            writeBodyJson(null);
        }
        writeWebJson(JSON.toJSONString(json));
    }

    public void writeJSONDetail(Object code, String msg, JSONObject json, SqlResultSet rs, JSONArray rslist) throws Exception {
        if(rs!=null){
            if(rs.getConParam().getTotal()==0) writeDataToBuf(rs);
            JSONArray s=rs.getConParam().getSumArray();
            if(s!=null) json.put("footer", s);
        }
        writeMsg(code, msg);
        writeHead(null);
        writeBodyJson(json);
        if(write==null) write=new BufferJSONWrite();
        write.setCharset(charset);
        write.setWcharset(wcharset);
        response.setContentType(CON_JSON);
        response.getOutputStream().write("{".getBytes(charset));
        writeBody(rs, rslist);
        response.getOutputStream().write("}".getBytes(charset));
        response.getOutputStream().flush();
        response.getOutputStream().close();
        write.close();
    }

    private void writeDetail(SqlResultSet rs, JSONArray rslist) throws Exception {
        if(rs!=null){
            if(rs.getConParam().getTotal()>0) writeDataToResponse(rs);
            else{
                write.getDetailData(new ReadBufferFileCallBackHandler(){
                    @Override
                    public boolean read(String text, int index) throws Exception {
                        response.getOutputStream().write(text.getBytes(charset));
                        return text!=null;
                    }
                });
            }
        } else if(rslist!=null){
            int len=rslist.size();
            for(int i=0; i<len; i++){
                if(i>0) response.getOutputStream().write(",".getBytes(charset));
                response.getOutputStream().write(JSON.toJSONStringWithDateFormat(rslist.get(i)).getBytes(charset));
            }
        }

    }

    private void writeHasBody(SqlResultSet rs, JSONArray rslist) throws Exception {
        if(objKey!=null){
            response.getOutputStream().write(("\""+body+"\":{").getBytes(charset));
        } else {
            response.getOutputStream().write(("\""+body+"\":").getBytes(charset));
        }

        JSONObject m=bodyjson;
        if(body!=null) m= (JSONObject) json.get(body);
        ArrayList<String> klist=m.getKeylist();
        int len=klist.size();
        for(int i=0;i<len;i++){
            String k=klist.get(i);
            if(!k.equals(objKey)){
                writeKey(klist.get(i), m.get(klist.get(i)), len, i);
            }
        }
        if(len>0) response.getOutputStream().write(",".getBytes(charset));
        if(objKey!=null){
            response.getOutputStream().write(("\""+objKey+"\":[").getBytes(charset));
        } else {
            response.getOutputStream().write(("[").getBytes(charset));
        }

        writeDetail(rs, rslist);
        response.getOutputStream().write("]".getBytes(charset));
        if(objKey!=null){
            response.getOutputStream().write("}".getBytes(charset));
        }
    }

    private void writeBody(SqlResultSet rs, JSONArray rslist) throws Exception {
        ArrayList<String> klist=json.getKeylist();
        int len=klist.size();
        for(int i=0;i<len;i++){
            String k=klist.get(i);
            if(!k.equals(body)){
                writeKey(k, json.get(k), len, i);
            }
        }
        if(body!=null) response.getOutputStream().write(",".getBytes(charset));
        if(body!=null){
            writeHasBody(rs, rslist);
        } else {
            if(objKey!=null){
                response.getOutputStream().write(("\""+objKey+"\":[").getBytes(charset));
            } else {
                response.getOutputStream().write(("[").getBytes(charset));
            }
            writeDetail(rs, rslist);
            response.getOutputStream().write("]".getBytes(charset));
        }
        write.close();
    }

    private void writeKey(String key, Object value, int len, int i) throws Exception {
        if(i>=1) if(len>0) response.getOutputStream().write(",".getBytes(charset));
        String txt;
        if(withDate) txt=JSONObject.toJSONStringWithDateFormat(value);
        else txt=JSONObject.toJSONString(value);
        response.getOutputStream().write(("\""+key+"\":"+txt).getBytes(charset));
    }

    private void writeDataToBuf(SqlResultSet rs) throws Exception {
        if(write==null) write=new BufferJSONWrite();
        write.setCharset(charset);
        while(rs.next()){
            write.writeDetailData(new WriteBody(){
                @Override
                public String write() throws Exception {
                    JSONObject m=rs.toRowJson();
                    if(rs.getConParam().getSqlCallback()!=null){
                        m=rs.getConParam().getSqlCallback().toJSON(rs.getConParam().getFromtab(), m);
                    }
                    return JSON.toJSONStringWithDateFormat(m);
                }
            });
        }
    }

    private void writeDataToResponse(SqlResultSet rs) throws Exception {
        int i=0;
        while(rs.next()){
            JSONObject m=rs.toRowJson();
            if(rs.getConParam().getSqlCallback()!=null){
                m=rs.getConParam().getSqlCallback().toJSON(rs.getConParam().getFromtab(), m);
            }
            if(i>0) response.getOutputStream().write(",".getBytes(charset));
            response.getOutputStream().write(JSON.toJSONStringWithDateFormat(m).getBytes(charset));
//            rs.toRowJsonBuf(response, charset);
//            response.getOutputStream().write(rs.toRowJsonStr().getBytes(charset));
            i++;
        }
    }

    /**
     * 下载文件
     * @param fi
     * @throws Exception
     */
    public void downFile(String fi) throws Exception {
        if("nginx".equals(SqlNodeVar.getXlsDownServer())) downFileN(fi);
        else downFileT(fi);
    }

    private void downFileT(String fi) throws Exception {
        BufferedInputStream bis = null;
        BufferedOutputStream bos = null;
        InputStream fis = null;
        OutputStream fos = null;
        try {
            File uploadFile = new File(fi);
            fis = new FileInputStream(uploadFile);
            bis = new BufferedInputStream(fis);
            //response.setContentType("application/vnd.ms-excel; charset=UTF-8");
            response.setContentType("application/octet-stream;charset=utf-8");
            fi = new String(fi.getBytes(), "ISO-8859-1");
            response.setHeader("Content-disposition", "attachment;filename ="+fi);
            response.resetBuffer();
            fos = response.getOutputStream();
            bos = new BufferedOutputStream(fos);
            int bytesRead = 0;
            byte[] buffer = new byte[4096];
            while((bytesRead = bis.read(buffer, 0, 4096)) != -1) {
                bos.write(buffer, 0, bytesRead);
            }
            bos.flush();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (bis != null)
                bis.close();
            if (fis != null)
                fis.close();
            if (bos != null)
                bos.close();
            if (fos != null)
                fos.close();
        }
    }

    private void downFileN(String fi) {
        response.setHeader("Content-Disposition", "attachment; filename=\""+fi+"\"");
        response.setHeader("Content-Type", "application/octet-stream");
        response.setHeader("X-Accel-Redirect", "/query/temp/");  //给nginx返回实际文件存在的地址
    }

}
